home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 01 - 1984 & 1985 / 01.02 Jan 85.sit / 01.02 Jan 85 / C Vol. 1 No. 2 / DemoWindow.C / DemoWindow.C
Encoding:
C/C++ Source or Header  |  1985-08-17  |  17.6 KB  |  666 lines  |  [TEXT/EDIT]

  1. /*
  2.  *  DemoWindow.C - Window stuff for Mac C Demo
  3.  *
  4.  *        Bob Denny
  5.  *        October, 1984
  6.  */
  7.  
  8. #include <Demo.H>
  9.  
  10. static int pen_down = FALSE;                    // Scrawl pen is not down yet
  11. static Point pold = {0,0};                        // Scrawl pen is at {0,0}
  12.  
  13. /*
  14.  * EDIT_WINDOW() - Create an "edit window" with all of the usual junk
  15.  *
  16.  * Allocates a WCB, creates the window from a resource template,
  17.  * complete with scrollers, grow box, go-away box, etc.  Sets
  18.  * the window as the current one and activates it.
  19.  */
  20. edit_window()
  21.     {
  22.     Rect view_rect;                                // TextEdit view rect
  23.     Rect bounds_rect;                                // Bounds for scrollers
  24.     Rect dest_rect
  25.     Rect *pr;                                        // --> port's rect
  26.     
  27.     dest_rect.top = 4;                            // *** TEMPORARY ***
  28.     dest_rect.left = 4;
  29.     dest_rect.bottom = 1000;
  30.     dest_rect.right = 500;
  31.     
  32.     if((dwp = (struct wcb *)get_wcb()) == NULL)
  33.         return;                                        // Ignore if no more WCB's
  34.     
  35.     //
  36.     // The following sets up the window's basic dimensions and
  37.     // rect's.  Each window starts out as a resource template, but
  38.     // at this point in the program, you could fiddle the window ...
  39.     // For example, you could change it's size or location, drag or
  40.     // grow rect etc.  The rest of the code drives off of the basic
  41.     // window dimensions.
  42.     //
  43.     dwp->wp = (WindowPtr)#GetNewWindow(WINDOW_ID, 0, -1);    // Get a copy of the window
  44.     #SetWTitle(dwp->wp, "\013Edit Window");        // Hack the title
  45.     #SetRect(&dwp->drag_rect, 4, 24, 508, 338);    // Fixed values in example
  46.     #SetRect(&dwp->grow_rect, 100, 60, 512, 302);
  47.     #SetPort(dwp->wp);                                    // Hook QuickDraw up to window
  48.     
  49.     //
  50.     // Set up TextEdit in the window
  51.     //
  52.     // For this program, the destRect is an arbitrary size
  53.     // large enough to demonstrate horizontal and vertical 
  54.     // scrolling.  The viewRect is set to the window's portRect
  55.     // less 4 pixels "bleed" on the left side and 15 pixels on the
  56.     // right and bottom for the scroll bars (thanks to calc_vrect()).
  57.     //
  58.     pr = &((dwp->wp)->portRect);
  59.     calc_vrect(pr, &view_rect);            // Calculate viewRect
  60.     dwp->te_handle = (TEHandle)#TENew(&dest_rect, &view_rect);
  61.     dwp->te_origin.v = dwp->te_origin.h = 4;
  62.  
  63.     //
  64.     // Add the scroll bars. Dynamically calculated from window
  65.     // dimensions.  Note that the controls must overlap the 
  66.     // window boundaries.  Start controls out hidden, activate will
  67.     // draw them.
  68.     //
  69.     bounds_rect.top = pr->top - 1;
  70.     bounds_rect.left = pr->right - 15;
  71.     bounds_rect.bottom = pr->bottom - 14;
  72.     bounds_rect.right = pr->right + 1;
  73.     dwp->vs_handle = (ControlHandle)#NewControl(dwp->wp, &bounds_rect,
  74.                                 "", TRUE,
  75.                                 dest_rect.top, dest_rect.top, dest_rect.bottom,
  76.                                 scrollBarProc, 1);
  77.     #ValidRect(&bounds_rect);
  78.  
  79.     bounds_rect.top = pr->bottom - 15;
  80.     bounds_rect.left = pr->left - 1;
  81.     bounds_rect.bottom = pr->bottom + 1;
  82.     bounds_rect.right = pr->right - 14;
  83.     dwp->hs_handle = (ControlHandle)#NewControl(dwp->wp, &bounds_rect,
  84.                                 "", TRUE,
  85.                                 dest_rect.left, dest_rect.left, dest_rect.right,
  86.                                 scrollBarProc, 1);
  87.     #ValidRect(&bounds_rect);
  88.  
  89.     //
  90.     // Finally, draw in the "grow icon"
  91.     //
  92.     #DrawGrowIcon(dwp->wp);
  93.     n_windows++;
  94.     }
  95.  
  96. /*
  97.  * SCRAWL_WINDOW - Create a "scrawl" window 
  98.  */
  99. scrawl_window()
  100.     {
  101.     if((dwp = (struct wcb *)get_wcb()) == NULL)
  102.         return;                                        // Ignore if no more WCB's
  103.     //
  104.     // See comments above ...
  105.     //
  106.     dwp->wp = (WindowPtr)#GetNewWindow(WINDOW_ID, 0, -1);    // Get a copy of the window
  107.     #SetWTitle(dwp->wp, "\015Scrawl Window");        // Hack the title
  108.     #SetRect(&dwp->drag_rect, 4, 24, 508, 338);    // Fixed values in example
  109.     #SetRect(&dwp->grow_rect, 100, 60, 512, 302);
  110.     #SetPort(dwp->wp);                            // Hook QuickDraw up to window
  111.     #BackPat(&(QD->black));                        // Background is black
  112.     #PenPat(&(QD->white));                        // Pen is white (redundant??)
  113.     #PenSize(2,2);                                    // 2x2 pixels
  114.     #EraseRect(&(dwp->wp->portRect));         // Establish new background
  115.     n_windows++;
  116.     return(0);
  117.     }
  118.  
  119. /*
  120.  * DELETE_WINDOW() - Remove window from screen & dispose of all structures
  121.  *
  122.  * Inputs:
  123.  *            dp    --> WCB of the window to delete
  124.  *
  125.  *    Outputs:
  126.  *                none
  127.  *
  128.  * Calls the #DisposeWindow service to remove the window from the screen
  129.  * and release all associated data structures, including the window
  130.  * record. NOTE - assumes window was created with record on heap.
  131.  * Then frees up the WCB associated with the window.
  132.  */
  133. delete_window(dp)
  134. struct wcb *dp;
  135.     {
  136.     
  137.     if(dp->te_handle != NULL)                        // If edit window
  138.         {
  139.         #TEDispose(dp->te_handle);                    // Return TE record to heap
  140.         dp->te_handle = NULL;                        // Mark TE inactive in WCB
  141.         }
  142.     #DisposeWindow(dp->wp);                            // Dispose of the window
  143.     dp->wp = NULL;                                        // Mark the WCB free
  144.     
  145.     // Add anything else here you may want when deleting a window
  146.     // such as a dialog or alert interaction.
  147.     
  148.     n_windows--;                                        // Count it gone
  149.     }
  150.  
  151. /*
  152.  *    CONTENT_CLICK() - Handle mouse down in window content region
  153.  *
  154.  * Inputs (global variables only)
  155.  *        dwp         -->    WCB of window in which mouse was clicked (verified)
  156.  *        Event.where        Mouse-down location (global coordinates)
  157.  *
  158.  * Outputs:
  159.  *        No explicit outputs
  160.  *
  161.  */
  162. content_click()
  163.     {
  164.     unsigned short ext;
  165.     ControlHandle ch;
  166.     unsigned short part;
  167.     long foo;
  168.     Rect *_vr;
  169.     int scroll_up();
  170.     int scroll_down();
  171.     
  172.     //
  173.     // Mouse-down in scrawl window changes the "old" position
  174.     // to the current position before allowing drawing.  Then 
  175.     // it turns on the pen-down flag.
  176.     //
  177.     if(dwp->te_handle == NULL)                    // If this is a "scrawl" window
  178.         {
  179.         #GetMouse(&pold);
  180.         pen_down = TRUE;
  181.         return(0);
  182.         }
  183.     
  184.     //
  185.     // We have a mouse-down in content of an edit window.
  186.     //
  187.     // If it's in the viewRect, do the TEClick.  Otherwise, handle
  188.     // the click in a control.
  189.     //
  190.     #GlobalToLocal(&Event.where);                // Convert coordinates to local
  191.     foo = #PtInRect(&Event.where, &((*(dwp->te_handle))->viewRect));
  192.     if(foo)
  193.         #TEClick(&Event.where, 0, dwp->te_handle); // 0 should be "ext"
  194.     else
  195.         {                                                // In control
  196.         part = (short)#FindControl(&Event.where, EvWindow, &ch);
  197.         switch(part)
  198.             {
  199.             case inUpButton:
  200.                 #TrackControl(ch, &Event.where, scroll_up);
  201.                 break;
  202.                 
  203.             case inDownButton:
  204.                 #TrackControl(ch, &Event.where, scroll_down);
  205.                 break;
  206.                 
  207.             case inPageUp:
  208.                 page_scroll(part, ch, -1);
  209.                 break;
  210.                 
  211.             case inPageDown:
  212.                 page_scroll(part, ch, 1);
  213.                 break;
  214.                 
  215.             case inThumb:
  216.                 #TrackControl(ch, &Event.where, 0);
  217.                 edit_scroll();
  218.             }
  219.         }
  220.     }
  221.  
  222. /*
  223.  * CONTENT_RELEASE() - Release of mouse in content region
  224.  *
  225.  * Only if scrawl window, toggle the draw/move flag
  226.  */
  227. content_release()
  228.     {
  229.     if(dwp->te_handle == NULL)
  230.         pen_down = FALSE;
  231.     return(0);
  232.     }
  233.  
  234. /*
  235.  * DO_SCRAWL() - Handle scrawling
  236.  *
  237.  * This is actually called fro the cursor handling
  238.  * routine, since it is needed whether or not the mouse
  239.  * is down.
  240.  */
  241. do_scrawl()
  242.     {
  243.     Point p;
  244.     
  245.     #GetMouse(&p);
  246.    if((pold.vh[0] == p.vh[0]) && (pold.vh[1] == p.vh[1]))
  247.       return;
  248.     pold.vh[0] = p.vh[0];
  249.     pold.vh[1] = p.vh[1];
  250.    if(pen_down)
  251.       #LineTo(p.vh[1],p.vh[0]);
  252.    else
  253.       #MoveTo(p.vh[1],p.vh[0]);
  254.     }
  255.  
  256. /*
  257.  * ACT_WIND() - Do activate stuff for window
  258.  */
  259. act_wind(wp)
  260. WindowPtr wp;                                        // Window to activate
  261.     {
  262.     struct wcb *find_wcb();
  263.     struct wcb *dp;
  264.     
  265.     if((dp = find_wcb(wp)) == NULL)            // Make this the current 
  266.         return(0);                                    // Not ours, ignore the event
  267.  
  268.     #SetPort(wp);                                    // This is current grafPort
  269.     
  270.     if(dp->te_handle == NULL)                    // If it's a scrawl window
  271.         {
  272.         // Nothing for now
  273.         }
  274.     else                                                // Do edit window
  275.         {
  276.         #DrawGrowIcon(wp);                        // Draw activated version
  277.         #TEActivate(dp->te_handle);            // Turn on text editing
  278.         #ShowControl(dp->vs_handle);            // Draw in scrollers
  279.         #ShowControl(dp->hs_handle);
  280.         }
  281.     dwp = dp;                                        // Set global pointer
  282.     }
  283.  
  284. /*
  285.  * DEACT_WIND() - Do deactivation stuff for window 
  286.  */
  287. deact_wind(wp)
  288. WindowPtr wp;
  289.     {
  290.     struct wcb *dp;
  291.     struct wcb *find_wcb();
  292.     
  293.     if((dp = find_wcb(wp)) == NULL)            // Find our WCB for this window
  294.         return(0);                                    // Oops, not ours
  295.         
  296.     #SetPort(wp);                                    // This is current grafPort
  297.     
  298.     if(dp->te_handle == NULL)                    // If it's a scrawl window
  299.         {
  300.         return(0);
  301.         }
  302.     else                                                // Do edit window
  303.         {
  304.         #DrawGrowIcon(wp);                        // Draw deactivated version
  305.         #TEDeactivate(dp->te_handle);            // Turn off text editing
  306.         #HideControl(dp->vs_handle);            // Hide scrollers
  307.         #HideControl(dp->hs_handle);
  308.         }
  309.     dwp = NULL;                                        // Clear master pointer
  310.     }
  311.     
  312. /*
  313.  * UPD_WIND() - Update window
  314.  *
  315.  * Note that this may be called whether or not the window
  316.  * is the front window and/or active.  This must not change
  317.  * our assumptions about the front window.
  318.  *
  319.  * More interesting is the fact that this routine gets called 
  320.  * continuously for each window.  If BeginUpdate sets the visRgn to 
  321.  * anything but the empty region, something gets drawn.
  322.  */
  323. upd_wind(wp)
  324. WindowPtr wp;
  325.     {
  326.     struct wcb *find_wcb();
  327.     struct wcb *dp;
  328.     
  329.     
  330.     if((dp = find_wcb(wp)) == NULL)            // Get the WCB for this window
  331.         return(0);                                    // (not ours??)
  332.     
  333.     #BeginUpdate(wp);                                // Fudge visRgn to update portion
  334.  
  335.     #SetPort(wp);                                    // Hook up to QuickDraw
  336.     
  337.     if(dp->te_handle == NULL)                    // If it's a scrawl window
  338.         #EraseRect(&(wp->portRect));             // Just wipe it clean
  339.     else                                                // Do edit window
  340.         {
  341.         #DrawGrowIcon(wp);                        // Draw the grow icon
  342.         #DrawControls(wp);                        // Draw all controls
  343.         //
  344.         // Note the syntax for addressing the rgnBbox of the window's
  345.         // grafPort's visRgn.  This is the recommended Rect to use
  346.         // when calling TEUpdate.  Who says C is easy to read?
  347.         //
  348.         #TEUpdate(&((*(wp->visRgn))->rgnBBox), dp->te_handle);
  349.         }
  350.     #EndUpdate(wp);                                // Restore visRgn
  351.     }
  352.  
  353. /*
  354.  * DO_GROW() - Handle grow/resize operations on window
  355.  *
  356.  * Inputs:
  357.  *        (G)    dwp -->     WCB of current window
  358.  *        (G)    Event        Event record for click in grow box
  359.  *
  360.  * Outputs:
  361.  *        none
  362.  *
  363.  * Much of this code is skipped for a scrawl window.  The operations
  364.  * done here generate an update event for the window.  Keep this in mind
  365.  * and try to avoid redundant drawing.  Above all -- avoid redrawing the
  366.  * entire window ... it's just too crude for the Mac.
  367.  */
  368. do_grow()
  369.     {
  370.     Rect *pr;
  371.     Rect temp_rect;
  372.     unsigned long grow_result;
  373.     unsigned short height, width;
  374.     
  375.     //
  376.     // Do the grow operation while mouse is held down.  When it is
  377.     // released, the new height and width of the window are returned
  378.     // packed in a longword.  Note that a zero result indicates no
  379.     // change, and we can skip this whole thing.
  380.     //
  381.     grow_result = #GrowWindow(dwp->wp,&Event.where, &dwp->grow_rect);
  382.     if(grow_result == 0)
  383.         return;
  384.     height = (unsigned short)#HiWord(grow_result);
  385.     width = (unsigned short)#LoWord(grow_result);
  386.     #SetPort(dwp->wp);                                // Hook up Quickdraw ...
  387.     pr = &((dwp->wp)->portRect);                    // pr --> new portRect of window
  388.     
  389.     if(dwp->te_handle != 0)                            // TextEdit windows only
  390.         {
  391.         //
  392.         // The scroll bars must be manually accumulated into the update
  393.         // region.  See the window manager manual for a fairly lucid
  394.         // explanation of this.  We also have to invalidate the size box.
  395.         // Note we are making assumptions here about the size and location
  396.         // of the scrollers.  This stuff handles the case where the window
  397.         // enlarges.  The calls to DrawControls and DrawGrowIcon in the 
  398.         // update event handler take care of the shrink case.
  399.         //
  400.         temp_rect.top = pr->top;
  401.         temp_rect.bottom = pr->bottom;
  402.         temp_rect.right = pr->right;
  403.         temp_rect.left = temp_rect.right - 16;    // Right hand 16 pixels
  404.         #InvalRect(&temp_rect);                        // Add it to the update region
  405.         temp_rect.top = temp_rect.bottom - 16;    // Size box
  406.         #EraseRect(&temp_rect);                        // Erase size box (WHY?????)
  407.         temp_rect.left = pr->left;                    // Bottom 16 pixels
  408.         #InvalRect(&temp_rect);                        // Add it to the update region
  409.         }
  410.     
  411.     //
  412.     // Now we re-size and set update on the winbdow.  Then move and
  413.     // resize the controls.  Finally, update the text-edit "viewRect".
  414.     //
  415.     #SizeWindow(dwp->wp, width, height, TRUE); // Resize & start update rgn
  416.  
  417.     if(dwp->te_handle != 0)                        // Only for TextEdit windows ...
  418.         {
  419.         #MoveControl(dwp->vs_handle, pr->right - 15, pr->top - 1);
  420.         #SizeControl(dwp->vs_handle, 16,(pr->bottom - pr->top - 13));
  421.  
  422.         #MoveControl(dwp->hs_handle, pr->left - 1, pr->bottom - 15); 
  423.         #SizeControl(dwp->hs_handle, (pr->right - pr->left - 13), 16);
  424.  
  425.         //
  426.         // Note the direct access to TE's view rect via the handle
  427.         // we stored in our WCB.
  428.         //
  429.         calc_vrect(pr, &((*(dwp->te_handle))->viewRect));    
  430.         }
  431.     }
  432.  
  433. /*
  434.  * CALC_VRECT() - Make a TextEdit viewRect from portRect
  435.  *
  436.  * Inputs:
  437.  *        pr    -->    Window's portRect
  438.  *        vr    -->    where to fill in viewRect
  439.  *
  440.  * Outputs:
  441.  *        vr->topLeft and vr->botRight are filled in
  442.  *
  443.  * The TextEdit viewRect is set up to provide 15-pixel strips along
  444.  * the right hand and bottom edges of the dditing window (for scrollers
  445.  * and the size box. It also provides a 4-pixel "bleed" at the left edge.
  446.  * and at the top.
  447.  */
  448. calc_vrect(pr, vr)
  449. Rect *pr;
  450. Rect *vr;
  451.     {
  452.     vr->top = pr->top + 4;
  453.     vr->left = pr->left + 4;
  454.     vr->bottom = pr->bottom - 15;
  455.     vr->right = pr->right - 15;
  456.     }
  457.  
  458. /*
  459.  * PAGE_SCROLL() - Scroll a page per indicator
  460.  *
  461.  *    Inputs:
  462.  *        part        Part code where first clicked down
  463.  *        ch            control handle
  464.  *        dir        direction (-1 = up, +1 = down)
  465.  *
  466.  * Outputs:
  467.  *        none
  468.  */
  469. page_scroll(part, ch, dir)
  470. short part;
  471. ControlHandle ch;
  472. short dir;
  473.     {
  474.     Point cur_pt;
  475.     short amount;
  476.     Rect *vr;
  477.     
  478.     //
  479.     // First, calculate the "page size" in pixels.  For V scroll, it is
  480.     // the viewRect height less the line height.  For H scroll, it is
  481.     // half the width of the viewRect.
  482.     //
  483.     vr = &((*(dwp->te_handle))->viewRect);    // vr -> current ViewRect
  484.     if(ch == dwp->vs_handle)                        // If this is the vertical scroller
  485.         amount = vr->bottom - vr->top - (short)get_lh();
  486.     else                                                // Horiz scroll
  487.         amount = (vr->right - vr->left) / 2;
  488.     amount *= dir;                                    // Change sign per requirements
  489.  
  490.     //
  491.     // Now we must locally handle the mouse per the Macintosh Interface
  492.     // Guidelines.  Look closely at this code and note what it really
  493.     // does ...
  494.     //
  495.     do                                                    // Do this once even if mouse is
  496.         {                                                //   up by now ...
  497.         #GetMouse(&cur_pt);                        // Get current mouse
  498.         if((short)#TestControl(ch, &cur_pt) != part)    // If out of original part
  499.             continue;                                // Don't do anything
  500.         #SetCtlValue(ch, #GetCtlValue(ch) + amount);    // Page control value
  501.         edit_scroll();                                // Page the text
  502.         } while(#StillDown());
  503.     }
  504.         
  505.  
  506. /*
  507.  * SCROLL_UP() - Scroll up a text edit window
  508.  *
  509.  * This routine is called back from the toolbox with (naturally)
  510.  * Pascal-flavored arguments on the stack.  I've taken this
  511.  * opportunity to demonstrate inline assembly language and how
  512.  * to act like a Pascal procedure.
  513.  *
  514.  * Note that the scrolled amount is equal to the line height as
  515.  * stored in the TextEdit record for both vertical and horizontal
  516.  * scrolling.  
  517.  */
  518.  scroll_up()
  519.      {
  520. #asm
  521. ;
  522. ; Inputs:
  523. ;            4(sp)            Part code (int)
  524. ;            6(sp)            Control handle (address)
  525. ;
  526. ; NOTE:    I have done the unforgivable & extracted the trap values
  527. ;            from the trap definitions rether than include the D file.
  528. ;            I'm impatient ...
  529. ;
  530.         Link        a6,#0
  531.         Move.W    8(a6),d0            ; D0 = part code (W)
  532.         Beq        @1                    ; (0 means out of part region)
  533.         Move.L    10(a6),-(sp)    ; Push control handle (for later)
  534.         Clr.W        -(sp)                ; Gets control value
  535.         Move.L    10(a6),-(sp)    ; Push control handle
  536.         DC.W        $A960                ; _GetCtlValue (value on stack)
  537.         Jsr        get_lh            ; D0 = text line height, pixels (easier in C)
  538.         Sub.W        d0,(sp)            ; (SP) = new control value
  539.         Bge        @0                    ; (ok, its positive)
  540.         Clr.W        (sp)                ; Stop control at 0 value
  541. @0:    DC.W        $A963                ; _SetCtlValue
  542.         Jsr        edit_scroll        ; call C text scroller
  543. @1:
  544.         Unlk        a6                    ; "standard" Pascal routine exit ...
  545.         Move.l    (sp)+,a0
  546.         Addq        #6,sp
  547.         Jmp        (a0)
  548. #endasm
  549.     }
  550.  
  551. /*
  552.  * SCROLL_DOWN() - Scroll down a text edit window
  553.  *
  554.  * This routine is called back from the toolbox with (naturally)
  555.  * Pascal-flavored arguments on the stack. See comments above.
  556.  */
  557.  scroll_down()
  558.      {
  559. #asm
  560. ;
  561. ; Inputs:
  562. ;            4(sp)            Part code (int)
  563. ;            6(sp)            Control handle (address)
  564. ;
  565.         Link        a6,#0                ; no local automatics
  566.         Move.W    8(a6),d0            ; D0 = part code (W)
  567.         Beq        @1                    ; (0 means out of part region)
  568.         Move.L    10(a6),-(sp)    ; Push control handle (for later)
  569.         Clr.W        -(sp)                ; Gets control value
  570.         Move.L    10(a6),-(sp)    ; Push control handle
  571.         DC.W        $A960                ; _GetCtlValue (value on stack)
  572.         Jsr        get_lh            ; D0 = text line height, pixels (easier in C)
  573.         Add.W        d0,(sp)            ; (SP) = new control value
  574.         DC.W        $A963                ; _SetCtlValue
  575.         Jsr        edit_scroll        ; call C text scroller
  576. @1:
  577.         Unlk        a6                    ; "standard" Pascal routine exit ...
  578.         Move.l    (sp)+,a0
  579.         Addq        #6,sp
  580.         Jmp        (a0)
  581. #endasm
  582.     }
  583.  
  584. /*
  585.  * EDIT_SCROLL() - Scroll edit window per control values
  586.  *
  587.  * Inputs:
  588.  *        Current value of te_origin in the WCB
  589.  *        Current values of the scrollers
  590.  *
  591.  * Outputs:
  592.  *        Origin points in the WCB are updated
  593.  *        Enforces a low limit of 0 on the controls
  594.  */
  595. edit_scroll()
  596.     {
  597.     short int dh, dv;
  598.     
  599.     dh = dwp->te_origin.h - (short int)#GetCtlValue(dwp->hs_handle);
  600.     if(dh <= dwp->te_origin.h)
  601.         dwp->te_origin.h -= dh;
  602.     else
  603.         dwp->te_origin.h = 4;
  604.     
  605.     dv = dwp->te_origin.v - (short int)#GetCtlValue(dwp->vs_handle);
  606.     if(dv <= dwp->te_origin.v)
  607.         dwp->te_origin.v -= dv;
  608.     else
  609.         dwp->te_origin.v = 4;
  610.     
  611.     #TEScroll(dh, dv, dwp->te_handle);
  612.     }
  613.  
  614. /*
  615.  * GET_LH() - Return text line height per TextEdit record
  616.  *
  617.  * Inputs:
  618.  *        dwp --> current WCB
  619.  *
  620.  *    Outputs:
  621.  *        returns text line height for scrolling
  622.  */
  623. get_lh()
  624.     {
  625.     return((*(dwp->te_handle))->lineHeight);  /* Easy in C!!! */
  626.     }
  627.  
  628. /*
  629.  * GET_WCB() - Allocate a WCB for a new window
  630.  *
  631.  * Inputs:
  632.  *        none
  633.  *
  634.  * Outputs:
  635.  *        Returns --> allocated WCB or NULL if none left
  636.  */
  637. get_wcb()
  638.     {
  639.     int i;
  640.     
  641.     for(i=0; i<MAX_WINDOWS; i++)
  642.         if(dw[i].wp == NULL)
  643.             {
  644.             dw[i].te_handle = NULL;            // Mark editing not ready this window
  645.             return(&dw[i]);
  646.             }
  647.     return(0);
  648.     }
  649.  
  650. /*
  651.  * FIND_WCB() - Find window control block for given window
  652.  *
  653.  * Returns WCB pointer or 0 if given window pointer does not
  654.  * belong to one of our WCB's.
  655.  */
  656. find_wcb(wp)
  657. WindowPtr wp;
  658.     {
  659.     int i;
  660.     
  661.     for(i=0; i<MAX_WINDOWS; i++)
  662.         if(dw[i].wp == wp)
  663.             return(&dw[i]);
  664.     return(0);
  665.     }
  666.